
/***************************** item_34: 区分接口继承和实习继承 **************************************/

// public 继承由两部分组成：函数接口继承和函数实现继承

// 为了更好地展示选择之间的差异，让我们考虑一个展现绘图程序中各种几何形状的 class 继承体系：
#include <string>

class Shape {
    public:
        virtual void draw() const = 0;
        virtual void error(const std::string& msg);
        int objectID() const;
        // ...
};

class Rectangle: public Shape {};
class Ellipse: public Shape {};

// Shape 是个抽象 class；它的 pure virtual 函数 draw 使它成为一个抽象 class。所以客户不能够创建 Shape class 的实体，只能创建其 
// derived class 的实体。尽管如此，Shape 还是强烈影响了所有以 public 形式继承它的 derived classes，因为：
// 成员函数接口总是会被继承。public 继承意味 is-a（是一种），所以对 base class 为真的任何事情一定也对其 derived classes 为真。因此
// 如果某个函数可施行于某个 class 身上，一定也可施行于其 derived classes 身上。

// 首先考虑 pure virtual 函数 draw：
class Shape {
    public:
        virtual void draw() const = 0;
        // ...
};

// pure virtual 函数有两个最突出的特性：它们必须被任何 "继承了它们" 的具象 class 重新声明，而且它们在抽象 class 中通常没有定义。
// **** 声明一个 pure virtual 函数的目的是为了让 derived classes 只继承函数接口。
// 这对 Shape::draw 函数是再合理不过的事了，因为所有 Shape 对象都应该是可绘出的，这是合理的要求。但 Shape class 无法为此函数提供
// 合理的缺省实现，毕竟椭圆形绘法异于矩形绘法。Shape::draw 的声明式乃是对具象 derived classes 设计者说，"你必须提供一个 draw 函数，
// 但我不干涉你怎么实现它。"

// 我们可以为 pure virtual 函数提供定义。也就是说可以为 Shape::draw 供应一份实现代码，C++ 并不会发出怨言，但调用它的唯一途径是 "调用
// 时明确指出其 class 名称"：
Shape *ps = new Shape;          //错误！Shape 的抽象的
Shape* ps1 = new Rectangle;     //没问题
ps1->draw();                    //调用 Rectangle::draw
Shape* ps2 = new Ellipse;       //没问题
ps2->draw();                    //调用 Ellipse::draw
ps1->Shape::draw();             //调用 Shape::draw
ps2->Shape::draw();             //调用 Shape::draw

// 简朴的 impure virtual 函数背后的故事和 pure virtual 函数有点不同。一如往常，derived classes 继承其函数接口，但 impure virtual
// 函数会提供一份实现代码，derived classes 可能覆写（override）它。

// **** 声明简朴的（非纯）impure virtual 函数的目的，是让 derived classes 继承该函数的接口和缺省实现

// 考虑 Shape::error 这个例子：
class Shape {
    public:
        virtual void error(const std::string& msg);
        // ...
};

// 其接口表示，每个 class 都必须支持一个 "当遇上错误时可调用" 的函数，但每个 class 可自由处理错误。如果某个 class 不想针对错误做出任何
// 特殊行为，它可以退回到 Shape class 提供的缺省错误处理行为。也就是说 Shape::error 的声明式告诉 derived classes 的设计者，"你必须
// 支持一个 error 函数，但如果你不想自己写一个，可以使用 Shape class提供的缺省版本"。

// 允许 impure virtual 函数同时指定函数声明和函数缺省行为，却有可能造成危险。欲探讨原因，我们考虑 XYZ 航空公司设计的飞机继承体系。该公司
// 只有 A 型和 B 型两种飞机，两者都以相同方式飞行。因此 XYZ 设计出这样的继承体系：
class Airport {

};
class Airplane {
    public:
        virtual void fly(const Airport& destination);
};
void Airplane::fly(const Airport& destination) {
    // 缺省代码，将飞机飞至指定的目的地
}
class ModelA: public Airplane {

};
class ModelB: public Airplane {

};

// 为了表示所有飞机都一定能飞，并阐明 "不同型飞机原则上需要不同的 fly 实现"，Airplane::fly 被声明为 virtual。然而为了避免在 ModelA
// 和 ModelB 中撰写相同代码，缺省飞行行为由 Airplane::fly 提供，它同时被 ModelA 和 ModelB继续。

// 当 XYZ 新增 C 型飞机，它与 A型以及 B型 飞行方式不同。
// XYZ 公司的程序员在继承体系中针对 C 型飞机增加了一个 class，但由于他们急着让新飞机上线服务，竟忘了重新定义其 fly 函数：
class ModelC: public Airplane {
    // ...                      //未声明 fly 函数
};

// 然后代码中有一些诸如此类的动作：
Airport PDX(...);               //PDX 是我家附近的机场
Airplane *pa = new ModelC;
// ...
pa->fly(PDX);                   //调用 Airplane::fly

// 这将酿成大灾难；这个程序员试图以 ModelA 或 ModelB 的飞行方式来飞 ModelC。

// 问题不在 Airplane::fly 有缺省行为，而在于 ModelC 在未明白说出 "我要" 的情况下就继承了该缺省行为。幸运的是我们可以轻易做到 "提供
// 缺省实现给 derived classes，但除非它们明白要求否则免谈"。此间伎俩在于切断 "virtual 函数接口" 和其 "缺省实现" 之间的连接。
// 下面是一种做法：
class Airplane {
    public:
        virtual void fly(const Airport& destination) = 0;
        // ...
    protected:
        void defaultFly(const Airport &destination);
};

void Airplane::defaultFly(const Airport& destination) {
    //缺省行为，将飞机飞至指定的目的地    
}

// 现在 Airplane::fly 已被改为一个 pure virtual 函数，只提供飞行接口。其缺省行为也出现在 Airplane class 中，但此次系以独立
// 函数 defaultFly 的姿态出现。若想使用缺省实现（例如ModelA 和 ModelB），可以在其 fly 函数中对 defaultFly 做一个 inline 调用：
class ModelA: public Airplane {
    public:
        virtual void fly(const Airport& destination)
        {
            defaultFly(destination);
        }
        // ...
};
class ModelB: public Airplane {
    public:
        virtual void fly(const Airport& destination)
        {
            defaultFly(destination);
        }
        // ...
};

// 现在 ModelC class 不可能意外继承不正确的 fly 实现代码了，因为 Airplane 中的 pure virtual 函数迫使 ModelC 必须提供自己的fly 版本：
class ModelC: public Airplane {
    public:
        virtual void fly(const Airport& destination);
        // ...
};
void ModelC::fly(const Airport& destination) {
    // 将C 型飞机飞至指定的目的地
}

// 这个方案并非安全无虞，程序员还是可能因为剪贴代码而招来麻烦，但它的确比原先的设计值得依赖。

// 有些人反对以不同的函数分别提供接口和缺省实现，像上述的 fly 和 defaultFly 那样。
// 我们可以利用 "pure virtual 函数必须在 derived classes 中重新声明，但他们也可以拥有自己的实现" 这一事实。下面便是 Airplane
// 继承体系如何给 pure virtual 函数一份定义：
class Airplane {
    public:
        virtual void fly(const Airport& destination) = 0;
        // ...
};

void Airplane::fly(const Airport& destination) {
    // 缺省行为，将飞机飞至指定的目的地
}

class ModelA: public Airplane {
    public:
        virtual void fly(const Airport& destination)
        {
            Airplane::fly(destination);
        }
        // ...
};
class ModelB: public Airplane {
    public:
        virtual void fly(const Airport& destination)
        {
            Airplane::fly(destination);
        }
        // ...
};
class ModelC: public Airplane {
    public:
        virtual void fly(const Airport& destination);
    // ...
};
void ModelC::fly(const Airport& destination) {
    // 将C 型飞机飞至指定的目的地
}

// 这几乎和前一个设计一模一样，只不过pure virtual 函数 Airplane::fly 替换了独立函数 Airplane::defaultFly。本质上，现在的
// fly 被分隔为两个基本要素：其声明部分表现的是接口（那是derived classes必须使用的），其定义部分则表现出缺省行为（那是 derived 
// classes 可能使用的，但只有在它们明确提出申请时才是）。如果合并 fly 和 defaultFly，就丧失了 "让两个函数享有不同保护级别" 的
// 机会：习惯上被设为 protected 的函数（defaultFly）如今成了 public（因为它在 fly 之中）。

// 最后，让我们看看 Shape 的 non-virtual 函数 objectID；
class Shape {
    public:
        int objectID() const;
        // ...
};

// 如果成员函数是个 non-virtual 函数，意味是它并不打算在 derived classes 中有不同的行为。实际上一个 non-virtual 成员函数所
// 表现的不变性凌驾其特异性，因为它表示不论 derived class 变得多么特异化，它的行为都不可以改变。就其自身而言：

// *** 声明 non-virtual 函数的目的是为了令 derived classes 继承函数的接口及一份强制性实现。

// 你可以把 Shape::objectID 的声明想做是: "每个 Shape 对象都有一个用来产生对象识别码的函数；此识别码总是采用相同计算方法，该
// 方法由 Shape::objectID 的定义式决定，任何 derived class 都不应该尝试改变其行为"。由于 non-virtual 函数代表的意义是不变性
// 凌驾特异性，所以它绝不该在 derived class 中被重新定义。

// pure-virtual 函数、simple（impure）virtual函数、non-virtual函数之间的差异，使你得以精确指定你想要 derived classes继承的
// 东西：只继承接口，或是继承接口和一份缺省实现，或是继承接口和一份强制实现。由于这些不同类型的声明意味根本意味不相同的事情，当你声明
// 你的成员函数时，必须谨慎选择。如果你确实履行，应该能够避免经验不足的 class 设计者最常犯的两个错误。

// 第一个错误是将所有函数声明为 non-virtual。这使得 derived classes 没有余裕空间进行特化工作。non-virtual 析构函数尤其会带来
// 问题。当然啦，设计一个并不想成为 base class 的 class 是绝对合理的，既然这样，将其所有成员函数都声明为 non-virtual 也很适当。
// 但这种声明如果不是忽略了 virtual 和 non-virtual 函数之间的差异，就是过度担心 virtual 函数的效率成本。实际上任何 class 如果
// 打算被用来当做一个 base class，都会拥有若干 virtual 函数。

// 另一个常见错误是将所有成员函数声明为 virtual。有时候这样做是正确的，例如 Interface classes。然而这也可能是 class 设计者缺乏
// 坚定立场的前兆。某些函数就是不该在 derived class 中被重新定义，果真如此你应该将那些函数声明为 non-virtual。没有人有权利妄称你
// 的 class 适用于任何人任何事任何物而他们只需要花点时间重新定义你的函数就可以享受一切。如果你的不变性凌驾特异性，别害怕说出来。

// 总结：
// 1. 接口继承和实现继承不同。在 public 继承之下，derived classes 总是继承 base class 的接口。
// 2. pure virtual 函数只具体指定接口继承。
// 3. 简朴的（非纯）impure virtual 函数具体指定接口继承及缺省实现继承。
// 4. non-virtual 函数具体指定接口继承以及强制性实现继承。